<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>18.2.2-将虚拟 DOM 渲染为 HTML 字符串</title>
</head>
<body>
    
</body>
    <script>
        /**
         * 1. 处理自闭合标签，它的术语叫作 void element
         *      对于 void element，由于它无须闭合标签，
         *      所以在为此类标签生成 HTML 字符串时，
         *      无须为其生成对应的闭合标签
         * 
        */


        const VOID_TAGS = 'area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr'.split(',')

       
        // 给出如下虚拟节点对象
        const ElementVNode = {
            type: 'div',
            props: {
                id: 'foo'
            },
            children: [
                { type: 'p', children: 'hello' }
            ]
        }

        // 在不考虑任何边界条件的情况下，实现 renderElementVNode
        function renderElementVNode(vnode){
            // 取出标签名称 tag 和标签属性 props，以及标签的子节点
            const { type: tag, props, children } = vnode
            // 判断是否是 void element
            const isVoidElement = VOID_TAGS.includes(tag) // 新增
            // 开始标签的头部
            let ret = `<${tag}`
            // 处理标签属性
            if (props) {
                for (const k in props) {
                    // 以 key="value" 的形式拼接字符串
                    ret += ` ${k}="${props[k]}"`
                }
            }

            // 如果是 void element，则自闭合
            ret += isVoidElement ? `/>` : `>` // 新增
            // 如果是 void element，则直接返回结果，无须处理 children，因为 void element 没有 children
            if (isVoidElement) return ret // 新增

            // 处理子节点
            // 如果子节点的类型是字符串，则是文本内容，直接拼接
            if (typeof children === 'string') {
                ret += children
            } else if (Array.isArray(children)) {
                // 如果子节点的类型是数组，则递归地调用 renderElementVNode 完成渲染
                children.forEach(child => {
                    ret += renderElementVNode(child)
                })
            }

            // 结束标签
            ret += `</${tag}>`

            // 返回拼接好的 HTML 字符串
            return ret
        }
        
        // 测试
        console.log(renderElementVNode(ElementVNode))
    </script>
</html>